#ifndef THIRDPARTY_LOGGING_LOGGER_H_
#define THIRDPARTY_LOGGING_LOGGER_H_
#pragma once
#include <sys/time.h>
#include <errno.h>
#include <functional>
#include "thirdparty/logging/logger_stream.h"
#include "thirdparty/logging/fixed_buffer.h"
#include <thread>
#include <iostream>
#include <sstream>

namespace logging {

#define LOG(level) if (level >= Logger::GetLogger()) Logger(__FILE__, __LINE__, level).stream()

enum LoggerLevel {
  TRACE = 0,
  DEBUG = 1,
  INFO  = 2,
  WARN   = 3,
  FATAL = 4,
};

extern const char* g_level_str[];

typedef std::function<void(char*, uint32_t)> OutputLog;

class Logger {
 public:
  Logger(const char* source_file, size_t line, LoggerLevel level) {
    uint64_t time_stamp_us = GetTimeStamp();
    uint64_t time_stamp_snd = time_stamp_us / 1000000;
    uint64_t time_usec = time_stamp_us%1000000;
    if (time_stamp_snd != last_seconds_) {
      time_t now = time(0);
      struct tm ltm;
      ::localtime_r(&now, &ltm);
      snprintf(time_str_, 64, "%4d%02d%02d %02d:%02d:%02d",
               1900 + ltm.tm_year, 1 + ltm.tm_mon,
               ltm.tm_mday, ltm.tm_hour, ltm.tm_min, ltm.tm_sec);
      last_seconds_ = time_stamp_snd;
    }
    stream_.Append(time_str_, 17);
    snprintf(stream_.Buffer().GetCurrent(), 64, ".%06lu ", time_usec);
    stream_.Buffer().MoveCursor(8);

    GetCurrentThreadId();
    stream_.Append(thread_idstring_, thread_id_len_);
    stream_.Append(g_level_str[level], 6);

    const char* file = nullptr;
    if ((file = __builtin_strrchr(source_file, '/')) != nullptr) {
      file = file + 1;
    } else {
      file = source_file;
    }
    uint32_t len = __builtin_strlen(file);
    stream_.Append(file, len);
    stream_ << ':' << line << ' ';
  }

  LoggerStream& stream() {
    return stream_;
  }
  ~Logger() {
    stream_ << "\n";
    output_function_(stream_.Buffer().CStr(), stream_.Buffer().Size());
  }
  static void SetLogger(LoggerLevel level) {
    level_ = level;
  }
  static LoggerLevel GetLogger() {
    return level_;
  }
  static void SetOutPutFun(OutputLog fun) {
    output_function_ = std::move(fun);
  }

  int GetCurrentThreadId() {
    if (thread_id_ == 0) {
      std::stringstream oss;
      oss << std::this_thread::get_id();
      oss >> thread_id_;
      thread_id_len_ = snprintf(thread_idstring_, 32, "%d ", thread_id_);
    }
    return thread_id_;
  }

 private:
  static uint64_t GetTimeStamp() {
    struct timeval tv;
    gettimeofday(&tv, NULL);//返回1970年至今的秒+微秒
    return tv.tv_sec * 1000000 + tv.tv_usec;
  }
  LoggerStream stream_;
  static OutputLog output_function_;
  static LoggerLevel level_;
  static thread_local uint64_t last_seconds_; // from 1970 snds
  static thread_local char time_str_[64];    //
  static thread_local int thread_id_;
  static thread_local char thread_idstring_[32];
  static thread_local uint32_t thread_id_len_;

};
}


#endif  // THIRDPARTY_LOGGING_LOGGER_H_
